package cn.ratel.aop;

import cn.ratel.cache.CacheKit;
import cn.ratel.component.Enhancers;
import cn.ratel.ioc.annotation.Controller;
import cn.ratel.tool.EnhancersBuilder;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ProxyKit {
	private static Logger log = LoggerFactory.getLogger(CacheKit.class);

	private static final EnhancerCallback ENHANCER_CALLBACK = new EnhancerCallback();
	private static Map<Method, List<? extends Enhancers.Enhancer>> methodEnhancerMap = new HashMap<>();
	private static Map<Class<? extends Enhancers.Enhancer>, Enhancers.Enhancer> enhancerInstanceMap = new HashMap<>();

	public static List<? extends Enhancers.Enhancer> getEnhancers(Method method) {
		return methodEnhancerMap.get(method);
	}

	public static <T> T enhance(Class<T> clazz) {
		Method[] methods = clazz.getDeclaredMethods();
		Preconditions.checkState(methods != null, "%s have no declared methods", clazz.getSimpleName());
		for (Method method : methods) {
			int modifiers = method.getModifiers();
			if (Modifier.isPublic(modifiers) && (!Modifier.isStatic(modifiers))) {
				List<Enhancers.Enhancer> result;
				if (clazz.isAnnotationPresent(Controller.class)) {
					result = EnhancersBuilder.build(Enhancers.me().getGlobalInterceptors(), clazz, method);
				} else {
					result = EnhancersBuilder.build(Enhancers.me().getGlobalEnhancers(), clazz, method);
				}
				log.debug("enhance method [{}] -- {}", method.toString(), result);
				methodEnhancerMap.put(method, result);
			}
		}
		return (T) net.sf.cglib.proxy.Enhancer.create(clazz, ENHANCER_CALLBACK);
	}

	public static Enhancers.Enhancer instance(Class<? extends Enhancers.Enhancer> clazz) {
		Enhancers.Enhancer enhancer = enhancerInstanceMap.get(clazz);
		if (enhancer == null) {
			try {
				enhancer = clazz.newInstance();
				enhancerInstanceMap.put(clazz, enhancer);
			} catch (InstantiationException | IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		}
		return enhancer;
	}
}
